D:\a\scloud-dns\scloud-dns\src\dns\packet\question\mod.rs
Line | Count | Source |
1 | | use crate::dns::q_class::DNSClass; |
2 | | use crate::dns::q_name::parse_qname; |
3 | | use crate::dns::q_type::DNSRecordType; |
4 | | use crate::exceptions::SCloudException; |
5 | | |
6 | | /// A DNS Question section |
7 | | #[derive(Debug, PartialEq, Clone)] |
8 | | pub struct QuestionSection { |
9 | | pub q_name: String, |
10 | | pub q_type: DNSRecordType, |
11 | | pub q_class: DNSClass, |
12 | | } |
13 | | |
14 | | impl QuestionSection { |
15 | | /// Serialize the DNS question section into a byte array |
16 | | /// |
17 | | /// # Exemple : |
18 | | /// ``` |
19 | | /// use crate::dns::packet::question::QuestionSection; |
20 | | /// use crate::dns::q_type::DNSRecordType; |
21 | | /// use crate::dns::q_class::DNSClass; |
22 | | /// |
23 | | /// let question = QuestionSection { |
24 | | /// q_name: "example.com".to_string(), |
25 | | /// q_type: DNSRecordType::A, |
26 | | /// q_class: DNSClass::IN, |
27 | | /// }; |
28 | | /// |
29 | | /// let bytes = question.to_bytes().unwrap(); |
30 | | /// |
31 | | /// // QNAME + QTYPE + QCLASS |
32 | | /// assert!(bytes.len() > 6); |
33 | | /// ``` |
34 | 3 | pub fn to_bytes(&self) -> Result<Vec<u8>, SCloudException> { |
35 | 3 | let mut buf = Vec::with_capacity(self.q_name.len() + 5); |
36 | | |
37 | | // Encode QNAME |
38 | 6 | for label in self.q_name.split('.')3 { |
39 | 6 | let len = label.len(); |
40 | 6 | if len > 63 { |
41 | 1 | return Err(SCloudException::SCLOUD_QUESTION_SERIALIZATION_FAILED_QNAME_TOO_LONG); |
42 | 5 | } |
43 | 5 | buf.push(len as u8); |
44 | 5 | buf.extend_from_slice(label.as_bytes()); |
45 | | } |
46 | 2 | buf.push(0x00); |
47 | | |
48 | 2 | let q_type_u16 = |
49 | 2 | u16::try_from(self.q_type).expect("Cannot convert QuestionSection q_type to u16"); |
50 | 2 | let q_class_u16 = u16::try_from(self.q_class).unwrap(); |
51 | | |
52 | 2 | buf.extend_from_slice(&q_type_u16.to_be_bytes()); |
53 | 2 | buf.extend_from_slice(&q_class_u16.to_be_bytes()); |
54 | | |
55 | 2 | Ok(buf) |
56 | 3 | } |
57 | | |
58 | | /// Deserialize the DNS question section from a byte array |
59 | | /// |
60 | | /// # Exemple : |
61 | | /// ``` |
62 | | /// use crate::dns::packet::question::QuestionSection; |
63 | | /// use crate::dns::q_type::DNSRecordType; |
64 | | /// use crate::dns::q_class::DNSClass; |
65 | | /// |
66 | | /// // example.com A IN |
67 | | /// let raw_question: Vec<u8> = vec![ |
68 | | /// 0x07, b'e', b'x', b'a', b'm', b'p', b'l', b'e', |
69 | | /// 0x03, b'c', b'o', b'm', |
70 | | /// 0x00, // End of QNAME |
71 | | /// 0x00, 0x01, // QTYPE = A |
72 | | /// 0x00, 0x01, // QCLASS = IN |
73 | | /// ]; |
74 | | /// |
75 | | /// let (question, consumed) = QuestionSection::from_bytes(&raw_question, 0).unwrap(); |
76 | | /// |
77 | | /// assert_eq!(question.q_name, "example.com"); |
78 | | /// assert_eq!(question.q_type, DNSRecordType::A); |
79 | | /// assert_eq!(question.q_class, DNSClass::IN); |
80 | | /// assert_eq!(consumed, raw_question.len()); |
81 | | /// ``` |
82 | 2 | pub fn from_bytes( |
83 | 2 | buf: &[u8], |
84 | 2 | offset: usize, |
85 | 2 | ) -> Result<(QuestionSection, usize), SCloudException> { |
86 | 2 | let (q_name, mut pos) = parse_qname(buf, offset).unwrap(); |
87 | | |
88 | 2 | if buf.len() < pos + 4 { |
89 | 0 | return Err(SCloudException::SCLOUD_QUESTION_DESERIALIZATION_FAILED); |
90 | 2 | } |
91 | | |
92 | 2 | let q_type = DNSRecordType::try_from(u16::from_be_bytes([buf[pos], buf[pos + 1]])).unwrap(); |
93 | | |
94 | 2 | let q_class = DNSClass::try_from(u16::from_be_bytes([buf[pos + 2], buf[pos + 3]])).unwrap(); |
95 | | |
96 | 2 | pos += 4; |
97 | | |
98 | 2 | Ok(( |
99 | 2 | QuestionSection { |
100 | 2 | q_name, |
101 | 2 | q_type, |
102 | 2 | q_class, |
103 | 2 | }, |
104 | 2 | pos - offset, |
105 | 2 | )) |
106 | 2 | } |
107 | | } |